﻿// Created by amoylel on 17/12/2018.
// Copyright (c) 2018 amoylel All rights reserved.

#ifndef AMO_UID_AA57E87A_39BA_4694_B808_470519656D15_HPP__
#define AMO_UID_AA57E87A_39BA_4694_B808_470519656D15_HPP__

#include <sstream>
#include <cstdint>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <mutex>

#include <amo/network.hpp>

namespace amo {
    class uid {
    public:
        uid() {
            id = generate_uid();
        }
        
        uid(const int64_t& rhs) : id(rhs) {
        
        }
        
        uid(const uid& rhs) : id(rhs.id) {
        
        }
        
        operator int64_t() const {
            return id;
        }
        
        std::string to_string() const {
            std::stringstream stream;
            stream << id;
            return stream.str();
        }
        
        static uid nil()  {
            return 0;
        }
        
        static int64_t timestamp_nanoseconds() {
            tm then_tm;
            then_tm.tm_year = 1970 - 1900;
            then_tm.tm_mon = 0; //1月
            then_tm.tm_mday = 1;
            then_tm.tm_hour = 0;
            then_tm.tm_min = 0;
            then_tm.tm_sec = 0;
            time_t timetThen = mktime(&then_tm); //得到时间的整数值
            std::chrono::system_clock::time_point start_time =
                std::chrono::system_clock::from_time_t(timetThen);
            return std::chrono::duration_cast<std::chrono::nanoseconds>
                   (start_time.time_since_epoch()).count();
                   
        }
        
        static int64_t generate_uid() {
            static std::mutex mutex;
            static int64_t epoch_ = 1514736000000LL; // 2018-01-01 00:00:00
            static int64_t  sequence_(0);
            static int64_t mac = ((amo::get_any_mac48() & 0x7F) << 17);
            static int64_t last_time = 0;
            static int64_t mask = 0x1FFFF;
            
            mutex.lock();
            int64_t sq = ++sequence_;
            
            int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>
                          (std::chrono::system_clock::now().time_since_epoch()).count();
                          
            if (sq % (mask + 1) == 0) {
            
                while (last_time >= now) {
                    std::this_thread::sleep_for(std::chrono::microseconds(10));
                    now = std::chrono::duration_cast<std::chrono::milliseconds>
                          (std::chrono::system_clock::now().time_since_epoch()).count();
                          
                }
            }
            
            last_time = now;
            
            mutex.unlock();
            
            
            // 取当前时间与基准时间的差值，该值可以保证32位整型保证17年以内的时间戳都不会被截断
            
            int64_t time = now - epoch_;
            
            // 高40位表示时间
            int64_t val = 0x7fffffffffffffffLL & (time << 24);
            
            //val |= (mac & 0x3F) << 18;										//中间7位是机器代码, 唯一标识
            val |= mac;
            val |= (sq & 0x1FFFF);												//最后17位是squenceID
            
            return val;
        }
        
        friend std::size_t hash_value(const uid& p) {
            std::hash<int64_t> hs;
            return hs(p.id);
        }
    private:
    
        int64_t id;
        
    };
}


namespace std {
    template <>
    struct hash<amo::uid> {
        size_t operator()(amo::uid const & x) const {
            std::hash<uint64_t> hs;
            return hs((uint64_t)x);
        }
    };
}

#endif //AMO_UID_AA57E87A_39BA_4694_B808_470519656D15_HPP__
